#ifndef __BULLETVEHICLE_H__
#define __BULLETVEHICLE_H__

#include <Ogre.h>
#include <btBulletDynamicsCommon.h>
#include "OpenSteer/AbstractVehicle.h"
#include "OpenSteer/SteerLibrary.h"

// LocalSpaceMixin implementa AbstractLocalSpace -----------------------

typedef OpenSteer::LocalSpaceMixin<OpenSteer::AbstractVehicle> LocalSpaceAV;

// SteerLibraryMixin permite calcular las fuerzas ----------------------

typedef OpenSteer::SteerLibraryMixin<LocalSpaceAV> SteerLibraryAV;

/***********************************************************************
 * Clase BulletVehicle: Permite utilizar métodos de SteerLibraryMixin  *
 * sobre un cuerpo rígido de Bullet.                                   *
 ***********************************************************************/

class BulletVehicle : public SteerLibraryAV {

 private:

  btRigidBody* _pRigidBody; // Puntero a cuerpo rígido de Bullet

  float _bodyRadius;        // Radio del cuerpo rígido
  float _maxForce;          // Fuerza máxima aplicada cuerpo rígido
  float _maxSpeed;          // Velocidad máxima del cuerpo rígido

 public:

  // Constructores y destructores ---------------------------------------

  BulletVehicle(btRigidBody* pRigidBody = NULL, float bodyRadius = 1,
                float maxForce = 0, float maxSpeed = 0);
  ~BulletVehicle() {}

  // Sobreescribir metodos position y setPosition de LocalSpaceMixin ---

  OpenSteer::Vec3 position() const;
  OpenSteer::Vec3 setPosition(OpenSteer::Vec3 newPosition);

  // Métodos propios ---------------------------------------------------

  // Get/Set para el cuerpo rígido
  btRigidBody* rigidBody() const { return _pRigidBody; }
  void setRigidBody(btRigidBody* pRigidBody) { _pRigidBody = pRigidBody; }

  // Aplica un vector de fuerza al cuerpo rígido, limitando dicha fuerza
  // a _maxForce, y actualizando la base ortonormal del vehículo.
  void applySteeringForce(const OpenSteer::Vec3 steeringForce);

  // Convertir OpenSteer::Vec3 a btVector3
  static btVector3 openSteerToBullet(const OpenSteer::Vec3);

  // Convertir OpenSteer::Vec3 a btVector3
  static OpenSteer::Vec3 bulletToOpenSteer(const btVector3);

  /********************************************************************
   * Implementación de AbstractVehicle                                *
   ********************************************************************/

  // Métodos utilizados por SteerLibrary -------------------------------

  float radius() const;             // Radio del vehículo
  OpenSteer::Vec3 velocity() const; // Vector velocidad
  float speed() const;              // Módulo del vector velocidad
                                    // Posición futura del vehículo
  OpenSteer::Vec3 predictFuturePosition(const float predictionTime) const;

  // Métodos presentes pero no utilizados por SteerLibrary -------------

  float maxForce() const;
  float maxSpeed() const;

  // Métodos no utilizados por SteerLibrary ----------------------------

  float mass() const { return 0; }      // No se utiliza
  float setMass(float m) { return 0; }  // No se utiliza
  float setRadius(float radius);
  float setSpeed(float newSpeed);
  float setMaxForce(float newMaxForce);
  float setMaxSpeed(float newMaxSpeed);

  // Método update para recalcular la fuerza ---------------------------

  // Implementación por defecto, no hacer nada
  virtual void update(const float currentTime, const float elapsedTime) {};
};

#endif
